// /////////////////////////////////////////////////////////////////////////////
//
// Name:            Serializable.cpp
// Author:          Michael Bartsch (ZCCdark203)
//
// Desc :           The Serializable class provides a base for classes that need
//                  serialization. By deriving from this class, the derived
//                  class gains all required methods for serialization.
//
// License:         Copyright (C) 2009 Michael Bartsch and Contributors
//
//                  This program is free software: you can redistribute it
//                  and/or modify it under the terms of the zlib/libpng License.
//                  See main.cpp for conditions of distribution and use.
//
// /////////////////////////////////////////////////////////////////////////////

// Include files
#include "Serializable.h"
#include "GameManager.h"
#include "SerializedAttributesImpl.h"


// Serializable class
// Serializable constructor.
Serializable::Serializable()
{
}

// Serializable deconstructor.
Serializable::~Serializable()
{
}

// Loads this object from a XML file.
bool Serializable::loadXML(const std::string &fileName)
{
    // Get pointer to the File System of Irrlicht.
    IFileSystem *fileSystem = GameManager::Instance()->getDevice()->getFileSystem();

    // Open the file.
    IReadFile *file = fileSystem->createAndOpenFile(fileName.c_str());

    if(!file)
      return false;

    IXMLReader *xml = fileSystem->createXMLReader(file);

    // Load the contents of the file.
    bool success = loadXML(xml);

    // Drop the file and return whether we've been successful or not.
    xml->drop();
    file->drop();

    return success;
}

// Loads this objects from a XML file through the given IXMLReader object.
bool Serializable::loadXML(IXMLReader *file)
{
    // Check if we got a valid pointer.
    if(file == NULL)
      return false;

    // Create a SerializedAttributes object to hold our attributes.
    SerializedAttributes *attributes = new SerializedAttributes();

    // Load contents from the XML file.
    while(file->read())
    {
       switch(file->getNodeType())
       {
          case io::EXN_ELEMENT:

             // Check how many attributes the current node has.
             // In case the current node has zero nodes, it indicates that it is an serialized object.
             if(file->getAttributeCount() == 0)
             {
                 SerializedAttributes *object = new SerializedAttributes();
                 object->loadXML(file);

                 attributes->pushObject(stringc(file->getNodeName()).c_str(), object);
                 object->drop();
             }

             // In case the current node has more than zero nodes, it indicates that we're dealing
             // with a "normal" serialized value.
             else
             {
                 SerializedAttribute *attribute = parseAttribute( stringc(file->getNodeName()).c_str(),
                                                                  stringc(file->getAttributeValue(L"type")).c_str(),
                                                                  stringc(file->getAttributeValue(L"value")).c_str() );
                 attributes->pushAttribute(attribute);
                 attribute->drop();
             }

             break;

          case io::EXN_ELEMENT_END:
          // This case has been encapsulated in order to gaurantee the initialisation of the
          // "success" variable.
          {
             // Deserialize the attributes.
             bool success = onDeserialization(attributes);

             // Clean up and return whether we've been successful or not.
             attributes->drop();

             return success;
          }

          default:
             break;
       }
    }

    // We might not have read all the required data, but we're going to try to deserialize it
    // as there's also chance that we've all the data we need.

    // Deserialize the attributes.
    bool success = onDeserialization(attributes);

    // Clean up and return whether we've been successful or not.
    attributes->drop();

    return success;
}

// Creates a new attribute with the given parameters.
SerializedAttribute* Serializable::parseAttribute(const std::string &name, const std::string &type,
                                                  const std::string &value)
{
    SerializedAttribute *attribute = NULL;

         if(type == "aabbox3d")           attribute = new SerializedAabbox3d(name, aabbox3df());
    else if(type == "bool")               attribute = new SerializedBool(name, false);
    else if(type == "color")              attribute = new SerializedColor(name, SColor());
    else if(type == "dimension2d")        attribute = new SerializedDimension2d(name, dimension2df());
    else if(type == "line2d")             attribute = new SerializedLine2d(name, line2df());
    else if(type == "line3d")             attribute = new SerializedLine3d(name, line3df());
    else if(type == "number")             attribute = new SerializedNumber(name, 0);
    else if(type == "plane3d")            attribute = new SerializedPlane3d(name, plane3df());
    else if(type == "quaternion")         attribute = new SerializedQuaternion(name, quaternion());
    else if(type == "rect")               attribute = new SerializedRect(name, rect<s32>());
    else if(type == "string")             attribute = new SerializedString(name, L"");
    else if(type == "vector2d")           attribute = new SerializedVector2d(name, vector2df());
    else if(type == "vector3d")           attribute = new SerializedVector3d(name, vector3df());

    if(attribute != NULL)
      attribute->setString(value);

    return attribute;
}

// Saves this object to a XML file.
bool Serializable::saveXML(const std::string &fileName)
{
    // Get pointer to the File System of Irrlicht.
    IFileSystem *fileSystem = GameManager::Instance()->getDevice()->getFileSystem();

    // Open the file.
    IWriteFile *file = fileSystem->createAndWriteFile(fileName.c_str());

    if(!file)
      return false;

    IXMLWriter *xml = fileSystem->createXMLWriter(file);

    // Write header to the file.
    xml->writeXMLHeader();

    // Save our attributes to the file.
    bool success = saveXML(xml);

    // Drop the file and return whether we've been successful or not.
    xml->drop();
    file->drop();

    return success;
}

// Saves this object to a XML file through the given IXMLWriter object.
bool Serializable::saveXML(IXMLWriter *file)
{
    // Check if we got a valid pointer.
    if(file == NULL)
      return false;

    // Create a SerializedAttributes object to hold our attributes.
    SerializedAttributes *attributes = new SerializedAttributes();

    // Serialize the attributes.
    if(!onSerialization(attributes))
    {
       attributes->drop();
       return false;
    }

    // Save contents to the XML file.
    while(!attributes->isEmpty())
    {
       // Pull an attribute from the attributes collection.
       SerializedAttribute *attribute = attributes->pullAttribute();

       // Check the type of the attribute.
       if(attribute->getType() == "object")
       {
          if(attribute->getName() != "")
          {
             file->writeElement( stringw( stringc( attribute->getName().c_str() ).c_str() ).c_str() );
             file->writeLineBreak();

             attribute->getObject()->saveXML(file);

             file->writeClosingTag( stringw( stringc( attribute->getName().c_str() ).c_str() ).c_str());
             file->writeLineBreak();
          }

          else
            attribute->getObject()->saveXML(file);
       }

       else
       {
          file->writeElement( stringw( stringc( attribute->getName().c_str() ).c_str() ).c_str(), true,
                              L"type", stringw( stringc( attribute->getType().c_str() ).c_str() ).c_str(),
                              L"value", attribute->getStringW().c_str());
          file->writeLineBreak();
       }

       // Drop the reference to the attribute.
       attribute->drop();
    }

    // Clean up.
    attributes->drop();

    return true;
}

// End of File
